#include "SymbolTable.h"
#include <iostream>
#include <sstream>
#include <vector>

using namespace std;


void SymbolTable::RecordImportantVariablePeak()
{
	ipeak = peak;
}

int SymbolTable::CheckVar(const char * var, bool isDeclaration, int line_num)
{
	int reg;

	if( isDeclaration )
	{
		if( LookupNew(var, reg) )
		{
			cerr << "Error(line " << line_num << "): Variable " << var << " redeclaration." << endl;
			exit(1);
		}
	}
	else
	{
		if( !LookupOld(var, reg) )
		{ 
			cerr << "Error(line " << line_num << "): Variable " << var << " undeclared." << endl;
			exit(1);
		}
	}

	return reg;
}

int SymbolTable::CheckFunc(const char * func, bool isDefinition, int line_num, list<int> paramlist)
{
	int reg;

	if( isDefinition )
	{
		if( LookupNew(func, reg) )
		{
			cerr << "Error(line " << line_num << "): Function " << func << " redeclaration." << endl;
			exit(1);
		}
		// Set the other things we need for a function: param list and label
		map<int,contents>::iterator it;
		it = symbols.find(reg);
		(*it).second.funclabel = "F_" + static_cast<string>(func) + "_label";
		(*it).second.params = paramlist;
	}
	else
	{
		if( !LookupOld(func, reg) )
		{ 
			cerr << "Error(line " << line_num << "): Function " << func << " undeclared." << endl;
			exit(1);
		}
	}

	return reg;
}

// LookupNew returns true if a newly declared variable is already in the symbol table (bad!).
//   reg will contain the variable's corresponding register
// LookupNew returns false if a newly declared variable is not in the symbol table (good)
//   The name of the register assigned to var will be returned in reg
bool SymbolTable::LookupNew(string var, int & reg)
{
	map<int,contents>::iterator it;

	// Search for matches on the variable name
	for (it = symbols.begin(); it != symbols.end(); it++)
	{
		if ( (*it).second.name == var && (*it).second.active)
		{
			// Variable found
			reg = (*it).first;
			return true;
		}
	}

	// Variable not found -- insert with reg as corresponding register
	reg = useNextRegister();
	contents temp;
	temp.name = var;
	temp.scope = scope;
	temp.active = true;
	temp.type = 'u';
	temp.funclabel = "";
	temp.return_address_scalar = useNextScalar('n');	// Note:  this will allocate a return scalar variable for regular variables as well as functions... oh well :)
	//symbols[reg] = temp;
	symbols.insert ( pair<int,contents>(reg,temp) );
	return false;
}

// LookupOld returns true if a previously declared variable is already in the symbol table (good!).
//   reg will contain the variable's corresponding register
// LookupOld returns false if a previously declared variable is not in the symbol table (bad)
//   reg will be an empty string
bool SymbolTable::LookupOld(string var, int & reg)
{
	map<int,contents>::iterator it;

	// Search for matches on the variable name
	for (it = symbols.begin(); it != symbols.end(); it++)
	{
		if ( (*it).second.name == var && (*it).second.active)
		{
			// Variable found
			reg = (*it).first;
			return true;
		}
	}

	// Variable not found 
	reg = -1;
	return false;
}

// ValidID returns true if we give it a valid id number and false if we do not
bool SymbolTable::ValidId(int & reg)
{
	if( symbols.find(reg) == symbols.end() )
		return false;
	return true;
}

// SetType finds "search" and switches on "type" to set type, vtype and etype
void SymbolTable::SetType(int id, std::string type)
{	
	map<int,contents>::iterator it;

	if( (it = symbols.find(id)) == symbols.end() )
	{
		cerr << "Can't set symbol type b/c symbol is not in table!" << endl;
		exit(1);
	}

	if ( it == symbols.end() )
	{
		cout << "Did not find id " << id << endl;
		return;
	}
	if (type == "array(char)")
	{
		(*it).second.type = 'c';
		(*it).second.vtype = 'a';
		(*it).second.semtype = AC;
	}
	else if (type == "string")
	{
		(*it).second.type = 'c';
		(*it).second.vtype = 'a';
		(*it).second.semtype = AC;
	}
	else if (type == "array(number)")
	{
		(*it).second.type = 'n';
		(*it).second.vtype = 'a';
		(*it).second.semtype = AN;
	}
	else if (type == "number")
	{
		(*it).second.type = 'n';
		(*it).second.vtype = 's';
		(*it).second.semtype = SN;
	}
	else if (type == "char")
	{
		(*it).second.type = 'c';
		(*it).second.vtype = 's';
		(*it).second.semtype = SC;
	}
	else
		cout << "No correct type has been assigned.\n";
}

// Claim the next available register for use; return its name
int SymbolTable::useNextRegister()
{
	int temp = iNextRegister;
	iNextRegister++;
	peak = iNextRegister;
	return temp;
}

string SymbolTable::useNextScalar(char type)
{
	if (type != 'n' && type != 'c')
	{
		cerr << "Error: You have tried to use 'useNextScalar(char type)' with type " << type << endl;
		exit(1);
	}
	etype tempSemtype;
	int reg = useNextRegister();
	contents temp;
	temp.active = true;
	temp.type = type;
	if (type == 'n' or type == 'N')
		tempSemtype = SN;
	else tempSemtype = SC;
	temp.vtype = 's';
	temp.semtype = tempSemtype;
	symbols.insert ( pair<int,contents>(reg,temp) );

	stringstream ss;
	string sTemp;
	ss << reg;
	ss >> sTemp;
	sTemp = "s" + sTemp;
	return sTemp;
}

string SymbolTable::useNextArray(char type)
{
	etype tempSemtype;
	int reg = useNextRegister();
	contents temp;
	temp.active = true;
	temp.type = type;
	if (type == 'n' or type == 'N')
		tempSemtype = AN;
	else tempSemtype = AC;
	temp.vtype = 'a';
	temp.semtype = tempSemtype;
	symbols.insert ( pair<int,contents>(reg,temp) );

	stringstream ss;
	string sTemp;
	ss << reg;
	ss >> sTemp;
	sTemp = "a" + sTemp;
	return sTemp;
}

int SymbolTable::useNextBlock()
{
	blockNum++;
	return blockNum - 1;
}

void SymbolTable::IncScope()
{
	scope++;
}

void SymbolTable::DecScope()
{
	if (scope <= 0 )
	{
		cout << "The scope is already " << scope << endl;
		return;
	}

	scope--;
	map<int,contents>::iterator it;
	
	for (it = symbols.begin(); it != symbols.end(); it++)
	{
		if ( (*it).second.scope > scope )
			(*it).second.active = false;
	}
}

void SymbolTable::DecFuncScope()
{
	if (scope <= 0 )
	{
		cout << "The scope is already " << scope << endl;
		return;
	}

	scope--;
}

void SymbolTable::PrintTable()
{
	map<int,contents>::iterator it;

	for (it = symbols.begin(); it != symbols.end(); it++)
	{
		cout << "Variable: " << (*it).first << endl;
		cout << "Name: " << (*it).second.name << endl;
		cout << "Scope: " << (*it).second.scope << endl;
		cout << "Type: " << (*it).second.type << endl;
		cout << "Variable Type: " << (*it).second.vtype << endl;
		cout << "Active: " << (*it).second.active << endl;
		cout << endl;
	}
}

// Get the enumerated type for an id number
etype SymbolTable::GetEType(int id)
{ return symbols[id].semtype; }

// Get the type, n or c for an id number
char SymbolTable::GetType(int & id)
{ return symbols[id].type; }

// Get the variable type, a or s, for an id number
char SymbolTable::GetVType(int id)
{ return symbols[id].vtype; }

std::string SymbolTable::getLabel(int id)
{ return symbols[id].funclabel; }

std::list<int> SymbolTable::getParameters(int id)
{ return symbols[id].params; }

void SymbolTable::ClearRegisters()
{
	vector< map<int, contents>::iterator > v;

	for( map<int, contents>::iterator it = symbols.begin(); it != symbols.end(); it++ )
	{
		if( it->first > ipeak )
			v.push_back(it);
	}

	for( vector< map<int, contents>::iterator >::reverse_iterator rit1 = v.rbegin(); rit1 != v.rend(); rit1++ )
		symbols.erase(*rit1);

	//symbols.clear();
	iNextRegister = 1;
	scope = 0;
	blockNum = 0;
}

int SymbolTable::getPeak()
{ return peak; }

std::vector<int> SymbolTable::getArrayIds()
{
	vector<int> int_v;
	for( map<int, contents>::iterator it = symbols.begin(); it != symbols.end(); it++ )
	{
		if( (*it).second.vtype == 'a' )
			int_v.push_back((*it).first);
	}
	return int_v;
}

int SymbolTable::getScope()
{ return scope; }

string SymbolTable::GetReturn(int id)
{ return symbols[id].return_address_scalar; }

